package com.baidu.iit.pxp.el.resolver;

import com.baidu.iit.pxp.el.ELContext;

import java.beans.FeatureDescriptor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
 * User: huangweili
 * Date: 14-4-29
 * Time: 下午1:49
 */
public class CompositeELResolver extends ELResolver {
    private final List<ELResolver> resolvers = new ArrayList<ELResolver>();

    public void add(ELResolver elResolver) {
        if (elResolver == null) {
            throw new NullPointerException("resolver must not be null");
        }
        resolvers.add(elResolver);
    }


    @Override
    public Class<?> getCommonPropertyType(ELContext context, Object base) {
        Class<?> result = null;
        for (ELResolver resolver : resolvers) {
            Class<?> type = resolver.getCommonPropertyType(context, base);
            if (type != null) {
                if (result == null || type.isAssignableFrom(result)) {
                    result = type;
                } else if (!result.isAssignableFrom(type)) {
                    result = Object.class;
                }
            }
        }
        return result;
    }


    @Override
    public Iterator<FeatureDescriptor> getFeatureDescriptors(final ELContext context, final Object base) {
        return new Iterator<FeatureDescriptor>() {
            Iterator<FeatureDescriptor> empty = Collections.<FeatureDescriptor> emptyList().iterator();
            Iterator<ELResolver> resolvers = CompositeELResolver.this.resolvers.iterator();
            Iterator<FeatureDescriptor> features = empty;

            Iterator<FeatureDescriptor> features() {
                while (!features.hasNext() && resolvers.hasNext()) {
                    features = resolvers.next().getFeatureDescriptors(context, base);
                    if (features == null) {
                        features = empty;
                    }
                }
                return features;
            }

            public boolean hasNext() {
                return features().hasNext();
            }

            public FeatureDescriptor next() {
                return features().next();
            }

            public void remove() {
                features().remove();
            }
        };
    }


    @Override
    public Class<?> getType(ELContext context, Object base, Object property) {
        context.setPropertyResolved(false);
        for (ELResolver resolver : resolvers) {
            Class<?> type = resolver.getType(context, base, property);
            if (context.isPropertyResolved()) {
                return type;
            }
        }
        return null;
    }

    @Override
    public Object getValue(ELContext context, Object base, Object property) {
        context.setPropertyResolved(false);
        for (ELResolver resolver : resolvers) {
            Object value = resolver.getValue(context, base, property);
            if (context.isPropertyResolved()) {
                return value;
            }
        }
        return null;
    }


    @Override
    public boolean isReadOnly(ELContext context, Object base, Object property) {
        context.setPropertyResolved(false);
        for (ELResolver resolver : resolvers) {
            boolean readOnly = resolver.isReadOnly(context, base, property);
            if (context.isPropertyResolved()) {
                return readOnly;
            }
        }
        return false;
    }

    @Override
    public void setValue(ELContext context, Object base, Object property, Object value) {
        context.setPropertyResolved(false);
        for (ELResolver resolver : resolvers) {
            resolver.setValue(context, base, property, value);
            if (context.isPropertyResolved()) {
                return;
            }
        }
    }


    @Override
    public Object invoke(ELContext context, Object base, Object method, Class<?>[] paramTypes, Object[] params) {
        context.setPropertyResolved(false);
        for (ELResolver resolver : resolvers) {
            Object result = resolver.invoke(context, base, method, paramTypes, params);
            if (context.isPropertyResolved()) {
                return result;
            }
        }
        return null;
    }
}
